﻿using System;
using System.Collections.Generic;
using System.Linq;
using HQPad.DAL;
using HQPad.Models;
using HQPad.Helpers;

namespace HQPad.Services
{
    public interface ICharacterService
    {
        IEnumerable<Character> ListCharacters();
        IEnumerable<Character> ListCharacters(string userName);
        Character SaveCharacter(Character character);
        Character GetCharacter(int characterId);
        ScratchPad CopyCharacterToScratchPad(int characterId, string sessionId);
        ScratchPad CreateScratchPadCharacter(string userName, string sessionId);
        ScratchPad CreateScratchPadCharacter(string userName, string sessionId, string name, string slug, string xml);
        IEnumerable<ScratchPad> ListScratchPadCharacters(string userName, string sessionId);
        void ClearScratchPad(string userName);
        ScratchPad GetScratchPadCharacter(int scratchPadId);
        ScratchPad GetScratchPadCharacter(string userName, string sessionId, string slug);
        void DeleteScratchPadCharacter(int scratchPadId);
        void UpdateScratchPadCharacter(ScratchPad character);
        Character SaveScratchPadAsCharacter(int scratchpadId);
        string CreateCharacterSlug(ScratchPad character);
        string CreateCharacterSlug(Character character);
        string CreateCharacterSlug(string name);
        void TidyScratchPad(string userName, string currentSessionId);
    }

    public class CharacterService : ICharacterService
    {
        private CharacterContext context = null;

        public CharacterService(IUnitOfWork context)
        {
            this.context = (CharacterContext)context;
        }



        #region CharacterService Methods

        #region CreateCharacterSlug
        public string CreateCharacterSlug(ScratchPad character)
        {
            return CreateCharacterSlug(character.CharacterName);
        }

        public string CreateCharacterSlug(Character character)
        {
            return CreateCharacterSlug(character.CharacterName);
        }

        public string CreateCharacterSlug(string name)
        {
            return
                name
                .Replace("_", "")
                .Replace("-", "")
                .Replace("&", "")
                .Replace("+", "")
                .Replace(",", "")
                .Replace(".", "")
                .Replace(" ", "-")
                .Replace("--", "-")
                .Replace("--", "-")
                .Replace("'", "");

        }
        #endregion

        #region ListCharacters
        /// <summary>
        /// Get all saved characters wrapped in lightweight context objects.
        /// </summary>
        /// <returns>The list of character context objects.</returns>
        public IEnumerable<Character> ListCharacters()
        {
            return (from c in context.Characters
                    select c);
        }
        #endregion

        #region ListCharacters
        /// <summary>
        /// Get all saved characters wrapped in lightweight context objects 
        /// for a specific user.
        /// </summary>
        /// <param name="userName">A uer name.</param>
        /// <returns>The list of character context objects.</returns>
        public IEnumerable<Character> ListCharacters(string userName)
        {
            return (from c in context.Characters
                    where c.UserName == userName
                    select c);
        }
        #endregion

        #region SaveCharacter
        /// <summary>
        /// Save a character.
        /// If the character is only on the scratch pad then a new
        /// character record is created.
        /// </summary>
        /// <param name="character">The character to update.</param>
        /// <returns>A character context with the saved data.</returns>
        public Character SaveCharacter(Character character)
        {
            bool success = false;
            Character savedCharacter = null;

            savedCharacter =
                (from c in context.Characters
                    where c.CharacterID == character.CharacterID
                    select c)
                .FirstOrDefault();

            this.context.Entry<Character>(savedCharacter).State = System.Data.EntityState.Modified;

            if (savedCharacter == null)
                throw new ApplicationException(string.Format("Invalid character ID {0}", character.CharacterID));

            // update the character data.
            savedCharacter.Xml = character.Xml;
            savedCharacter.CharacterName = character.CharacterName;
            savedCharacter.DateSaved = DateTime.Now;
            savedCharacter.UserName = character.UserName;

            return savedCharacter;
        }
        #endregion

        #region GetCharacter
        /// <summary>
        /// Get a saved character by id.
        /// </summary>
        /// <param name="characterId">A character id.</param>
        /// <returns>The character requested.</returns>
        public Character GetCharacter(int characterId)
        {
            return (from c in context.Characters
                    where c.CharacterID == characterId
                    select c)
                    .FirstOrDefault();
        }
        #endregion

        #region CopyCharacterToScratchPad
        /// <summary>
        /// Copy and existing saved character onto the scratch pad.
        /// If it already exists in the curent session on the scratch pad
        /// then return the existing one instead.
        /// </summary>
        /// <param name="characterId">The character id.</param>
        /// <param name="sessionId">The current user session id.</param>
        /// <returns>The character from the scratch pad.</returns>
        public ScratchPad CopyCharacterToScratchPad(int characterId, string sessionId)
        {
            Character character = GetCharacter(characterId);

            if (character == null)
                throw new ApplicationException(string.Format("Invalid character ID {0}", characterId));

            // Try and get the current character from the scratch pad.
            ScratchPad scratchCharacter = (from s in context.ScratchPad
             where s.SessionID == sessionId
                 && s.CharacterID == characterId
             select s)
             .FirstOrDefault();

            // if existing scratch character update it and return context.
            if (scratchCharacter != null)
            {
                scratchCharacter.Xml = character.Xml;
                scratchCharacter.DateSaved = DateTime.Now;
                scratchCharacter.UserName = character.UserName;

                this.context.Entry<ScratchPad>(scratchCharacter).State = System.Data.EntityState.Modified;
            }
            else
            {
                // create a new scratch pad record.
                scratchCharacter = new ScratchPad()
                {
                    CharacterID = characterId,
                    DateSaved = DateTime.Now,
                    SessionID = sessionId,
                    UserName = character.UserName,
                    Xml = character.Xml,
                    Slug = character.Slug,
                    CharacterName = character.CharacterName
                };

                context.ScratchPad.Add(scratchCharacter);
            }

            return scratchCharacter;
        }
        #endregion

        #region CreateScratchPadCharacter
        /// <summary>
        /// Create a new scratch pad character with default content.
        /// </summary>
        /// <param name="userName">The user name which owns this character</param>
        /// <param name="sessionId">The user scratch pad session id.</param>
        /// <returns>The default user created.</returns>
        public ScratchPad CreateScratchPadCharacter(string userName, string sessionId)
        {
            // Create a default character xml.
            string xml = CharacterRulesHelper.CreateDefaultCharacter().ToString();

            // Create scratch pad record.
            ScratchPad scratchCharacter = new ScratchPad()
            {
                DateSaved = DateTime.Now,
                SessionID = sessionId,
                UserName = userName,
                CharacterName = "mystery hero",
                Slug = CreateCharacterSlug("mystery hero"),
                Xml = xml
            };

            context.ScratchPad.Add(scratchCharacter);

            return scratchCharacter;
        }

                /// <summary>
        /// Create a new scratch pad character with default content.
        /// </summary>
        /// <param name="userName">The user name which owns this character</param>
        /// <param name="sessionId">The user scratch pad session id.</param>
        /// <param name="character">A previously unsaved scratch pad character.</param>
        /// <returns>The default user created.</returns>
        public ScratchPad CreateScratchPadCharacter(string userName, string sessionId, string name, string slug, string xml)
        {
            // Create scratch pad record.
            ScratchPad scratchCharacter = new ScratchPad()
            {
                DateSaved = DateTime.Now,
                SessionID = sessionId,
                UserName = userName,
                CharacterName = name,
                Slug = slug,
                Xml = xml
            };

            context.ScratchPad.Add(scratchCharacter);

            return scratchCharacter;
        }
        #endregion

        #region ListScratchPadCharacters
        /// <summary>
        /// Get all scratch pad characters for a user sessions.
        /// </summary>
        /// <param name="userName">A user name.</param>
        /// <param name="sessionId">The current users session id.</param>
        /// <returns>All characters on the scratch pad for the user.</returns>
        public IEnumerable<ScratchPad> ListScratchPadCharacters(string userName, string sessionId)
        {
            return (from s in context.ScratchPad
                    where s.UserName == userName && s.SessionID == sessionId
                    select s);
        }
        #endregion

        #region ClearScratchPad
        /// <summary>
        /// Remove all scratch pad record for a user.
        /// </summary>
        /// <param name="userName">A user name.</param>
        public void ClearScratchPad(string userName)
        {
            IEnumerable<ScratchPad> characters =
                (from s in context.ScratchPad
                 where s.UserName == userName
                 select s);

            foreach (ScratchPad character in characters)
                this.context.Entry<ScratchPad>(character).State = System.Data.EntityState.Deleted;
        }
        #endregion

        #region GetScratchPadCharacter
        /// <summary>
        /// Get a character off the scratch pad.
        /// </summary>
        /// <param name="scratchPadId">The id of the character on the scratch pad.</param>
        /// <returns>The character from the scratch pad.</returns>
        public ScratchPad GetScratchPadCharacter(int scratchPadId)
        {
            return (from s in context.ScratchPad
                    where s.ScratchPadID == scratchPadId
                    select s)
                    .FirstOrDefault();
        }

        /// <summary>
        /// Get a character off the scratch pad.
        /// </summary>
        /// <param name="scratchPadId">The id of the character on the scratch pad.</param>
        /// <returns>The character from the scratch pad.</returns>
        public ScratchPad GetScratchPadCharacter(string userName, string sessionId, string slug)
        {
            return (from s in context.ScratchPad
                    where s.UserName == userName &&
                        s.SessionID == sessionId &&
                        s.Slug == slug
                    select s)
                    .FirstOrDefault();
        }        
        #endregion

        #region DeleteScratchPadCharacter
        /// <summary>
        /// Delete a character off the scratch pad.
        /// </summary>
        /// <param name="scratchPadId">The id of a scratch pad character.</param>
        public void DeleteScratchPadCharacter(int scratchPadId)
        {
            // Try and get the current character from the scratch pad.
            ScratchPad scratchCharacter = 
                (from s in context.ScratchPad
                 where s.ScratchPadID == scratchPadId
                 select s)
             .FirstOrDefault();

            if (scratchCharacter == null)
                throw new ApplicationException(string.Format("Invalid scratch pad ID {0}", scratchPadId));

            this.context.Entry<ScratchPad>(scratchCharacter).State = System.Data.EntityState.Deleted;
        }
        #endregion

        #region UpdateScratchPadCharacter
        /// <summary>
        /// Update the content of a scrath pad character.
        /// </summary>
        /// <param name="scratchPadId">A scratch pad character id.</param>
        /// <param name="character">The character to save.</param>
        public void UpdateScratchPadCharacter(ScratchPad character)
        {
            //ScratchPad scratchCharacter = 
            //    (from s in context.ScratchPad
            //    where s.ScratchPadID == scratchPadId
            //    select s)
            //   .FirstOrDefault();

            //if (scratchCharacter == null)
            //    throw new ApplicationException(string.Format("Invalid scratch pad ID {0}", scratchPadId));

            character.DateSaved = DateTime.Now;
            //scratchCharacter.Xml = character.Xml;
            //scratchCharacter.Slug = CreateCharacterSlug(character.CharacterName);

            this.context.Entry<ScratchPad>(character).State = System.Data.EntityState.Modified;
        }
        #endregion

        #region SaveScratchPadAsCharacter
        /// <summary>
        /// Copy a scratch pad character to a saved character.
        /// If the scratch character is linked to an existing 
        /// saved character then update it.
        /// </summary>
        /// <param name="scratchpadId">The id of a scratch pad character.</param>
        public Character SaveScratchPadAsCharacter(int scratchpadId)
        {
            // Get the scratch pad character.
            ScratchPad scratchCharacter = GetScratchPadCharacter(scratchpadId);

            Character savedCharacter = null;

            if (!scratchCharacter.CharacterID.HasValue)
            {
                // create a new character record.
                savedCharacter = new Character();

                context.Characters.Add(savedCharacter);
            }
            else
            {
                // get existing character record.
                savedCharacter = 
                    (from c in context.Characters
                     where c.CharacterID == scratchCharacter.CharacterID.Value
                     select c)
                    .FirstOrDefault();

                if (savedCharacter == null)
                    throw new ApplicationException(string.Format("Invalid character ID {0}", scratchCharacter.CharacterID.Value));

                this.context.Entry<Character>(savedCharacter).State = System.Data.EntityState.Modified;
            }

            // check for conflict of name slug in characters.

            // update content.
            savedCharacter.CharacterName = scratchCharacter.CharacterName;
            savedCharacter.UserName = scratchCharacter.UserName;
            savedCharacter.Xml = scratchCharacter.Xml;
            savedCharacter.DateSaved = DateTime.Now;
            savedCharacter.Slug = scratchCharacter.Slug;

            return savedCharacter;
        }
        #endregion

        /// <summary>
        /// Remove any out of date scratch pad characters for this user.
        /// </summary>
        /// <param name="userName">The authenticated user name.</param>
        /// <param name="currentSessionId">The users current session id. Any other session id's are out of date.</param>
        public void TidyScratchPad(string userName, string currentSessionId)
        {
            IEnumerable<ScratchPad> characters =
               (from s in context.ScratchPad
                where s.UserName == userName && s.SessionID != currentSessionId
                select s);

            foreach (ScratchPad character in characters)
                this.context.Entry<ScratchPad>(character).State = System.Data.EntityState.Deleted;
        }
        
        #endregion



    }
}